1 package edu.jiangxin.apktoolbox.convert.base;
2
3 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
4 import edu.jiangxin.apktoolbox.utils.Constants;
5 import org.apache.commons.lang3.StringUtils;
6
7 import javax.swing.*;
8 import javax.swing.event.DocumentEvent;
9 import javax.swing.event.DocumentListener;
10 import javax.swing.text.*;
11 import java.awt.*;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.io.Serial;
15 import java.math.BigInteger;
16 import java.util.ArrayList;
17 import java.util.List;
18
19 public class BaseConvertPanel extends EasyPanel {
20 @Serial
21 private static final long serialVersionUID = 1L;
22
23 private static final String PROPERTY_KEY = "name";
24
25 private static final String DECIMAL = "Decimal";
26
27 private static final String BINARY = "Binary";
28
29 private static final String OCTAL = "Octal";
30
31 private static final String HEX = "Hex";
32
33 private transient DocumentListener documentListener;
34
35 private transient DocumentFilter documentFilter;
36
37 private boolean isChangedByUser;
38
39 private transient final List<BaseUiObject> baseUiObjects = new ArrayList<>();
40
41 @Override
42 public void initUI() {
43 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
44 setLayout(boxLayout);
45
46 documentListener = new TextFieldDocumentListener();
47 documentFilter = new TextFieldDocumentFilter();
48
49 isChangedByUser = true;
50
51 baseUiObjects.add(new BaseUiObject(2));
52 baseUiObjects.add(new BaseUiObject(8));
53 baseUiObjects.add(new BaseUiObject(10));
54 baseUiObjects.add(new BaseUiObject(16));
55
56 for (BaseUiObject baseUiObject : baseUiObjects) {
57 add(baseUiObject.panel);
58 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
59 }
60
61 JButton clearBtn = new JButton("Clear");
62 clearBtn.addActionListener(new ClearButtonActionListener());
63 add(clearBtn);
64 }
65
66
67 class ClearButtonActionListener implements ActionListener {
68 @Override
69 public void actionPerformed(ActionEvent arg0) {
70 isChangedByUser = false;
71 for (BaseUiObject baseUiObject : baseUiObjects) {
72 baseUiObject.textField.setText("");
73 }
74 isChangedByUser = true;
75 }
76 }
77
78 class TextFieldDocumentListener implements DocumentListener {
79 @Override
80 public void changedUpdate(DocumentEvent e) {
81 }
82
83 @Override
84 public void insertUpdate(DocumentEvent e) {
85 detect(e);
86 }
87
88 @Override
89 public void removeUpdate(DocumentEvent e) {
90 detect(e);
91 }
92 }
93
94 class TextFieldDocumentFilter extends DocumentFilter {
95 @Override
96 public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
97 Document doc = fb.getDocument();
98 StringBuilder sb = new StringBuilder();
99 sb.append(doc.getText(0, doc.getLength()));
100 sb.insert(offset, string);
101
102 Object propertyObj = doc.getProperty(PROPERTY_KEY);
103 if (propertyObj == null) {
104 Toolkit.getDefaultToolkit().beep();
105 return;
106 }
107 Integer radix = (Integer) doc.getProperty(PROPERTY_KEY);
108 if (isValidText(radix, sb.toString())) {
109 super.insertString(fb, offset, string, attr);
110 } else {
111 Toolkit.getDefaultToolkit().beep();
112 }
113 }
114
115 @Override
116 public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
117 Document doc = fb.getDocument();
118 StringBuilder sb = new StringBuilder();
119 sb.append(doc.getText(0, doc.getLength()));
120 sb.replace(offset, offset + length, text);
121
122 Object propertyObj = doc.getProperty(PROPERTY_KEY);
123 if (propertyObj == null) {
124 Toolkit.getDefaultToolkit().beep();
125 return;
126 }
127 Integer radix = (Integer) doc.getProperty(PROPERTY_KEY);
128 if (isValidText(radix, sb.toString())) {
129 super.replace(fb, offset, length, text, attrs);
130 } else {
131 Toolkit.getDefaultToolkit().beep();
132 }
133 }
134 }
135
136 private void detect(DocumentEvent documentEvent) {
137 Document doc = documentEvent.getDocument();
138 Integer radix = (Integer) doc.getProperty(PROPERTY_KEY);
139 convertMe(radix);
140 }
141
142 private void convertMe(int radix) {
143 if (!isChangedByUser) {
144 return;
145 }
146
147 BigInteger value = null;
148 for (BaseUiObject baseUiObject : baseUiObjects) {
149 if (baseUiObject.radix == radix) {
150 String text = baseUiObject.textField.getText();
151 if (StringUtils.isEmpty(text)) {
152 value = null;
153 } else {
154 value = new BigInteger(text, radix);
155 }
156 }
157 }
158 isChangedByUser = false;
159 for (BaseUiObject baseUiObject : baseUiObjects) {
160 if (baseUiObject.radix != radix) {
161 if (value == null) {
162 baseUiObject.textField.setText("");
163 } else {
164 baseUiObject.textField.setText(value.toString(baseUiObject.radix));
165 }
166 }
167 }
168 isChangedByUser = true;
169 }
170
171 private boolean isBinStr(String str) {
172 if (str == null) {
173 return false;
174 }
175 for (int i = 0; i < str.length(); i++) {
176 if ((str.charAt(i) != '1') && (str.charAt(i) != '0')) {
177 return false;
178 }
179 }
180 return true;
181 }
182
183 private boolean isDecStr(String str) {
184 if (str == null) {
185 return false;
186 }
187 for (int i = 0; i < str.length(); i++) {
188 if ((str.charAt(i) < '0') || (str.charAt(i) > '9')) {
189 return false;
190 }
191 }
192 return true;
193 }
194
195 private boolean isOctStr(String str) {
196 if (str == null) {
197 return false;
198 }
199 for (int i = 0; i < str.length(); i++) {
200 if ((str.charAt(i) < '0') || (str.charAt(i) > '7')) {
201 return false;
202 }
203 }
204 return true;
205 }
206
207 private boolean isHexStr(String str) {
208 if (str == null) {
209 return false;
210 }
211 for (int i = 0; i < str.length(); i++) {
212 char c = str.charAt(i);
213 if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f')) {
214 return false;
215 }
216 }
217 return true;
218 }
219
220 private boolean isValidText(int radix, String text) {
221 switch (radix) {
222 case 2 -> {
223 return isBinStr(text);
224 }
225 case 8 -> {
226 return isOctStr(text);
227 }
228 case 10 -> {
229 return isDecStr(text);
230 }
231 case 16 -> {
232 return isHexStr(text);
233 }
234 default -> {
235 return false;
236 }
237 }
238 }
239
240 class BaseUiObject {
241 int radix;
242
243 JPanel panel;
244
245 JLabel label;
246
247 JTextField textField;
248
249 BaseUiObject(int radix) {
250 this.radix = radix;
251 panel = new JPanel();
252 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
253
254 label = new JLabel(this + ":");
255 label.setPreferredSize(new Dimension(50, 25));
256
257 textField = new JTextField();
258 textField.getDocument().addDocumentListener(documentListener);
259 ((PlainDocument) textField.getDocument()).setDocumentFilter(documentFilter);
260 textField.getDocument().putProperty(PROPERTY_KEY, radix);
261
262 panel.add(label);
263 panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
264 panel.add(textField);
265 }
266
267 @Override
268 public String toString() {
269 switch (radix) {
270 case 2 -> {
271 return BINARY;
272 }
273 case 8 -> {
274 return OCTAL;
275 }
276 case 10 -> {
277 return DECIMAL;
278 }
279 case 16 -> {
280 return HEX;
281 }
282 default -> {
283 return String.valueOf(radix);
284 }
285 }
286 }
287 }
288 }